#region FileHeader
//////////////////////////////////////////////////////////////////////////////////
// Author:                          Hector Llanos
// Date:                            08.27.2010
// FileName:                        Game1.cs
// Reason:                          main game loop
//////////////////////////////////////////////////////////////////////////////////
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

using GameState;
using Manager;
using Base;
using MessagingSystem;
using Objects;
using Math;
using Event_System;
using Globals;

namespace Run_Game
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {

        #region PRIVATE_VARS
        GraphicsDeviceManager graphics;
        private static Game1 m_pInstance;

        private const int screenwidth = 1296;
        private const int screenheight = 900;

        private SpriteBatch spriteBatch;
        
        private List<IGameState> m_vStateArr;
        private static bool m_bPaused = false;

        private ObjectFactory<string, CBase> m_pObjectFactory;
        private ObjectFactory<string, CCharacterBase> m_pCharacterFactory;
        private ObjectManager m_pObjectManager;
        private CMessageSystem m_pMessageSystem;
        private CEventSystem m_pEventSystem;
        private CCharacterBase.CharacterType[] m_CharacterSelect;


        #endregion
        #region PROPERTIES
        public CCharacterBase.CharacterType[] CharacterSelect
        {
            get { return m_CharacterSelect; }
            set { m_CharacterSelect = value; }
        }
        public CEventSystem EventSystem
        {
            get { return m_pEventSystem; }
            set { m_pEventSystem = value; }
        }
        public CMessageSystem MessageSystem
        {
            get { return m_pMessageSystem; }
        }
        public ObjectFactory<string, CBase> ObjectFactory
        {
            get { return m_pObjectFactory; }
        }
        public ObjectFactory<string, CCharacterBase> CharacterFactory
        {
            get { return m_pCharacterFactory; }
            set { m_pCharacterFactory = value; }
        }
        public ObjectManager ObjectManager
        {
            get { return m_pObjectManager; }
        }
        public SpriteBatch SpriteBatch
        {
            get { return spriteBatch; }
            set { spriteBatch = value; }
        }
        public GraphicsDeviceManager Graphics
        {
            get { return graphics; }
            set { graphics = value; }
        }
        public static Game1 Instance
        {
            get
            {
                return m_pInstance;
            }
        }
        #endregion
        #region State_Functions
        public void ClearState()
        {
            for (int i = 0; i < m_vStateArr.Count; i++)
            {
                m_vStateArr[i].Exit();
            }
            m_vStateArr.Clear();
        }
        public void PopState()
        {
            m_vStateArr[m_vStateArr.Count - 1].Exit();
            m_vStateArr.RemoveAt(m_vStateArr.Count - 1);

        }
        public void PushState(IGameState state)
        {
            if (state != null)
            {
                state.Enter(Content, spriteBatch);
                m_vStateArr.Add(state);
            }

        }
        public void ChangeState(IGameState state)
        {

            ClearState();
            PushState(state);
        }
        #endregion

        public Game1()
        {
            m_pInstance = this;

            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = screenwidth;
            graphics.PreferredBackBufferHeight = screenheight;

            Content.RootDirectory = "Content";

            m_vStateArr = new List<IGameState>();

            m_CharacterSelect = new CCharacterBase.CharacterType[(int)CGlobal.Players.MAXPLAYERS];
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            base.Initialize();

            m_pObjectFactory = ObjectFactory<string, CBase>.Instance;
            m_pCharacterFactory = ObjectFactory<string, CCharacterBase>.Instance;

            m_pObjectManager = ObjectManager.Instance;

            m_pMessageSystem = CMessageSystem.Instance; 
            m_pMessageSystem.InitMessageSystem(MessageProc);

            m_pEventSystem = CEventSystem.Instance;
            m_pEventSystem.ClearEvents();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            ChangeState(new CMainMenuState());


            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
            m_pMessageSystem.ShutdownMessageSystem();
            m_pEventSystem.ShutdownEventSystem();
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {

            if (m_bPaused == false)
            {
                for (int i = 0; i < m_vStateArr.Count; i++)
                {
                    m_vStateArr[i].Input();
                }
            }
            else
            {
                m_vStateArr[1].Input();
            }

            // update
            if (m_bPaused == false )
            {

                for (int i = 0; i < m_vStateArr.Count; i++)
                {
                    m_vStateArr[i].Update(gameTime.ElapsedGameTime);
                }
            }
            else
            {
                m_vStateArr[1].Update(gameTime.ElapsedGameTime);
            }

            m_pMessageSystem.ProcessMessages();
            m_pEventSystem.ProcessEvents();

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Green);
            spriteBatch.Begin(SpriteSortMode.Texture, BlendState.AlphaBlend );
            BlendState bs = new BlendState();
            bs.ColorWriteChannels = ColorWriteChannels.All;
            //GraphicsDevice.BlendState.ColorWriteChannels = ColorWriteChannels.All;
            // TODO: Add your drawing code here
            for (int i = 0; i < m_vStateArr.Count; i++)
            {
                m_vStateArr[i].Render();
            }

            spriteBatch.End();
            base.Draw(gameTime);
        }

        void MessageProc(CBaseMessage pMsg)
        {
            //sort through all the messages
            switch (pMsg.MessageID)
            {
                case (int)CBaseMessage.Messages.MSG_CREATE_BULLET:
                    {
                        CCreateBulletMessage cCBM = (CCreateBulletMessage)pMsg;

                        CBulletBase bullet = (CBulletBase)m_pObjectFactory.CreateObject(cCBM.Type.ToString());

                        bullet.Owner = cCBM.Owner;
                        bullet.Type = cCBM.Type;
                        bullet.Damage = 5;
                        bullet.Heading = cCBM.Owner.Heading;

                        bullet.Position = bullet.Owner.Position;
                        bullet.Position  -= new Vector2(bullet.Owner.Width / 2, bullet.Owner.Height / 2);

                        Vector2 oldBulletVel = -bullet.Owner.GetForward(), bulletVel;

                        oldBulletVel.Normalize();

                        bulletVel = CMathLib.Vector2DRotate(oldBulletVel, (float)System.Math.PI);
                        bulletVel.X *= (bullet.Speed + System.Math.Abs( bullet.Owner.Velocity.X));
                        bulletVel.Y *= (bullet.Speed + System.Math.Abs(bullet.Owner.Velocity.Y));

                        bullet.Velocity = bulletVel;

                        m_pObjectManager.AddObject(bullet);
                        break;
                    }
                case (int)CBaseMessage.Messages.MSG_DESTORY_BULLET:
                    {
                        CDestroyBulletMessage cDBM = (CDestroyBulletMessage)pMsg;
                        m_pObjectManager.RemoveObject(cDBM.Bullet);

                        break;
                    }
                case (int)CBaseMessage.Messages.MSG_DESTORY_ENEMY:
                    {
                        CDestoryEnemy cDE = (CDestoryEnemy)pMsg;
                        m_pObjectManager.RemoveObject(cDE.Enemy);
                        break;
                    }
                case (int)CBaseMessage.Messages.MSG_Player1_HEALTH_CHANGE:
                    {
                        //get the hud state
                        CHudState hud = (CHudState)m_vStateArr[m_vStateArr.Count - 1];

                        //get/set mr bang's health 
                        CPlayer1HealthChange cMBHC = (CPlayer1HealthChange)pMsg;
                        hud.Player1Health = (int)CGlobal.Lerp(0, (float)cMBHC.MaxHealth, (float)cMBHC.Health / (float)cMBHC.MaxHealth);
                        hud.Player1HealthTrigger = true;
                        break;
                    }
                case (int)CBaseMessage.Messages.MSG_Player2_HEALTH_CHANGE:
                    {
                        //get the hud state
                        CHudState hud = (CHudState)m_vStateArr[m_vStateArr.Count - 1];

                        //get/set miss pew's health 
                        CPlayer2HealthChange cMBHC = (CPlayer2HealthChange)pMsg;
                        hud.Player2Health = (int)CGlobal.Lerp(0, (float)cMBHC.MaxHealth, (float)cMBHC.Health / (float)cMBHC.MaxHealth);
                        hud.Player2HealthTrigger = true;
                        break;
                    }
            };
        }
    }
}
